Skip to content

S09-06 Webpack5-开发脚手架

[TOC]

开发脚手架

知识点

commander

依赖包: commander :完整的 node.js 命令行解决方案。

  • 安装:pnpm i commander

API:

  • cmd.commad()(command, description?, options?),用于定义命令的一个方法。它允许你添加子命令及其选项,以便创建功能丰富的命令行工具。

    • commandstring,表示命令的名称及其参数。例如,'add <num1> <num2>'

    • description?string,描述命令的功能。通常用于帮助信息。

    • options?object,指定命令的配置选项,如是否允许未知选项等。

    • js
      program
        .command('greet <name>')
        .description('Greet a person')
        .action((name) => {
          console.log(`Hello, ${name}!`);
        });
      
      // $ coderwhy greet xxx
  • cmd.option()(flags, description, defaultValue?, callback?),用于定义命令行选项的方法。选项是以 --option-o 的形式传递给命令行工具的参数,允许用户在运行命令时修改其行为或设置。

    • flagsstring,选项的标志,通常以 -- 开头(例如 --verbose)。可以包含短选项(例如 -v)和长选项(例如 --verbose),用逗号分隔。

    • descriptionstring,对选项的描述,用于生成帮助信息。

    • defaultValueany,选项的默认值(可选)。如果用户未提供该选项,则使用此默认值。

    • callback() => void,可选的转换函数,用于处理选项值(例如将字符串转换为布尔值)。

    • js
      program.option('--count <number>', 'Number of items', 5, (val) => parseInt(val, 10));
      
      program.parse(process.argv);
      
      const options = program.opts();
      console.log(`Count: ${options.count}`);
      
      // $ coderwhy --count 10
  • cmd.opts()(),用于获取已解析的选项的值的方法。在定义命令行选项之后,你可以使用 program.opts() 来访问这些选项的值。

    • 返回:

    • optionsobject,包含所有已解析选项的键值对。键是选项的名称,值是用户提供的选项值。

    • js
      program
        .option('-p, --port <port>', 'Port number', 3000)
        .option('-d, --debug', 'Enable debugging');
      
      program.parse(process.argv);
      
      const options = program.opts();
      console.log(`Port: ${options.port}`);
      console.log(`Debug mode: ${options.debug}`);
      
      // $ coderwhy --port 8000 --debug
  • cmd.description()(description),用于为命令或整个程序设置描述的方法。用于说明命令或程序的功能和用途。

    • descriptionstring,用于描述命令或程序的功能。这是对用户的说明信息,通常显示在帮助文档中。

    • js
      program
        .version('1.0.0')
        .description('A simple CLI tool to manage tasks');
      
      // $ coderwhy --version
  • cmd.action()callback,用于定义命令执行逻辑的方法。它用于指定在命令被触发时应执行的操作。该方法允许你将函数与命令绑定起来,以便在用户调用特定命令时执行自定义逻辑。

    • callback(...args, options?) => void,指定在命令被触发时应执行的操作。

      • argsany,在 action 函数中,这些参数对应于命令中的位置参数(例如 <num1> <num2>)。
      • options?object,如果命令包含选项,它们会在 args 数组的末尾。
    • js
      program
        .command('greet <name>')
        .option('-p, --port <port>', 'port to listen on', '3000')
        .description('Greet a person')
        .action((name, options) => {
          console.log(`Hello, ${name}!,port is ${options.opts().port}`);
        });
      
      // $ coderwhy greet xxx
  • cmd.parse()(args?, options?),用于解析命令行输入的核心方法。它处理命令行参数、选项和命令,并根据这些输入执行相应的操作。

    • args?array默认:process.argv,表示命令行参数数组。默认情况下,如果未提供该参数,commander 会使用 process.argv,即 Node.js 进程的命令行参数。

    • options?{from?, allowUnknownOption?, unknownOption?},用于配置解析行为(比如是否显示错误信息)。

      • from?,指定要解析的起始位置,通常用于处理传递给 program.parse() 的参数数组。
      • allowUnknownOption?boolean默认:false,是否允许未知的选项。如果为 truecommander 将忽略未知选项而不会抛出错误。
      • unknownOption?(option) => void,当检测到未知选项时的处理函数。
    • js
      program.parse(process.argv, {
        allowUnknownOption: true,
        unknownOption: (option) => {
          console.error(`Unknown option: ${option}`);
        }
      });
  • cmd.version()(version, flags?, description?),用于设置和获取 CLI 工具的版本号。

    • versionstring,表示工具的版本号(如 '1.0.0')。

    • flags?string,指定用于显示版本号的选项(如 '-v, --version')。

    • description?string,用于为版本选项提供描述。

    • js
      program.version('2.3.4', '-v, --version', 'Show version information');
      
      // $ coderwhy --version
  • cmd.on()(event, listener),用于注册事件监听器,处理命令行解析过程中的事件。

    • eventstring,事件名称。常见的事件有:

      • command:*:触发在每个命令执行之前。可以用来捕获所有命令的执行前事件。
      • command:<commandName>:触发特定命令执行前的事件。例如,'command:serve' 会在 serve 命令执行之前触发。
      • beforeExit:在程序退出之前触发,可以用于执行清理任务。
      • --help:用于监听用户请求帮助信息时的事件,执行自定义的逻辑或输出额外的信息。
    • listener(command?) => void,事件发生时调用的回调函数。

    • js
      const { Command } = require('commander');
      const program = new Command();
      
      program
        .command('deploy')
        .description('Deploy the application')
        .action(() => {
          console.log('Application deployed');
        });
      
      // 1. 每次执行任何命令时,都会触发 'command:*' 事件,并输出所执行的命令。
      program.on('command:*', () => {
        console.log('Deploy command is about to be executed');
      });
      
      // 2. 每次执行 deploy 命令时,都会触发 'command:deploy' 事件,并输出特定消息。
      program.on('command:deploy', () => {
        console.log('Deploy command is about to be executed');
      });
      
      // 3. 当程序即将退出时,会触发 'beforeExit' 事件,并输出退出前的消息。
      program.on('beforeExit', () => {
        console.log('Program is about to exit');
      });
      
      program.parse(process.argv);

download-git-repo

依赖包:

  • download-git-repo:用于从 Git 仓库中下载项目或文件。只下载指定的版本或分支,而不涉及 Git 版本控制系统本身的操作。
    • 安装:pnpm i download-git-repo

API:

  • downloadGitRepo()(repo, dest, options?, callback?),用于从 Git 仓库中下载项目或文件。

    • repostring,要下载的 Git 仓库的 URL。可以是 GitHub、GitLab、Bitbucket 等 Git 服务器的仓库地址。

      • 注意: 地址上需要添加direct:#main
    • deststring,下载到的本地路径。

    • options?DownloadOptions | Function,额外的选项或一个回调函数。如果提供了回调函数,options 可以省略。

      • cloneboolean默认:false,如果需要克隆而不是下载
    • callback?(err) => void,下载完成后的回调函数。

    • js
      const downloadGitRepo = require('download-git-repo');
      
      // 指定分支或标签
      const repo = 'direct:github:user/repo#branch'; // 或 'direct:github:user/repo#tag'
      const destination = 'path/to/destination';
      
      // 克隆仓库而不是仅下载
      downloadGitRepo(repo, destination, {clone: true}, (err) => {
        if (err) {
          console.error('Download failed:', err);
        } else {
          console.log('Branch or tag downloaded successfully!');
        }
      });

EJS

EJS (Embedded JavaScript) 是一种用于生成动态 HTML 内容的模板引擎。它允许在 HTML 模板中嵌入 JavaScript 代码,以实现动态内容的渲染。

语法
  • 输出变量

  • <%= variable %>:输出变量的值。默认将输出转义为 HTML 实体,防止 XSS 攻击。

  • <%- variable %>:输出变量的值。不进行转义,直接输出原始 HTML。

  • 执行JS代码

  • <% code %>:执行 JavaScript 代码,但不输出。

  • 条件语句

  • <% if (condition) { %>...<% } %>:执行条件逻辑。

  • 循环语句

  • <% array.forEach(function(item) { %> ...<% }) %>:遍历数组。

  • 包含文件

  • <%- include('filename') %>:包含其他 EJS 模板文件。

示例:

  • header.ejs

    html
    <header>
      <h1>My Website</h1>
    </header>
  • index.ejs

    html
    <!DOCTYPE html>
    <html>
    + <%- include('header') %>
    <body>
      <h1>Hello, <%= name %>!</h1>
          
      <% if (isAdmin) { %>
        <p>You have admin access.</p>
      <% } %>
      <ul>
        <% items.forEach(function(item) { %>
          <li><%= item %></li>
        <% }); %>
      </ul>
    </body>
    </html>
API

依赖包:

  • ejs:EJS语法的扩展API
    • 安装:pnpm i ejs

语法:

  • ejs.compileFile()(filename, options),用于编译一个 EJS 模板文件为一个可重用的模板函数。该函数可以在多次调用时渲染模板。

    • filenamestring,模板文件的路径。

    • optionsobject,编译选项对象,用于配置模板的编译行为。

      • filename?string,用于设置 include 的查找路径。通常与模板文件的路径相同。
      • cache?boolean默认:,是否启用缓存。
      • delimiter?string默认:%,定义 <% %> 中的分隔符。
      • escape?boolean默认:true,是否对输出进行 HTML 转义。
      • views?string,视图路径,用于 include 的查找。
    • 返回:

    • compiledFunction(obj) => void,返回一个函数,调用这个函数时会将传递的数据渲染到模板中。函数接受一个数据对象作为参数,并返回渲染后的 HTML 字符串。

    • js
      const ejs = require('ejs');
      
      // 编译模板文件
      const compiledFunction = ejs.compileFile('template.ejs', { cache: true, delimiter: '%', escape: false });
      
      // 使用编译后的模板函数渲染数据
      const html = compiledFunction({ name: 'World' });
      
      console.log(html); // 输出渲染后的 HTML
  • ejs.renderFile()(filename, data, options?, callback),用于从文件渲染 EJS 模板,并返回结果。

    • filenamestring,模板文件的路径。

    • dataobject,传递给模板的数据对象。

    • options?object,渲染选项对象,用于配置模板的渲染行为。

      • filename?string,用于设置 include 的查找路径。通常与模板文件的路径相同。
      • cache?boolean默认:,是否启用缓存。
      • delimiter?string默认:%,定义 <% %> 中的分隔符。
      • escape?boolean默认:true,是否对输出进行 HTML 转义。
      • views?string,视图路径,用于 include 的查找。
    • callback(err, str) => void,渲染完成后的回调函数。

    • js
      const ejs = require('ejs');
      
      // 渲染模板文件
      ejs.renderFile('template.ejs', { name: 'World' }, { cache: true, delimiter: '%', escape: false }, (err, str) => {
        if (err) {
          console.error(err);
          return;
        }
        console.log(str); // 输出渲染后的 HTML
      });
  • ejs.compile()(templateString, options?),用于将模板字符串编译成一个可执行的函数。这个函数可以在运行时接受数据并渲染最终的 HTML。

    • templateStringstring,要编译的 EJS 模板字符串。

    • options?object,配置对象,包含以下属性:

      • filename?string,模板文件的路径。如果指定,错误信息中将包含文件路径。
      • cache?boolean默认:,是否启用缓存。
      • delimiter?string默认:%,定义 <% %> 中的分隔符。
      • escape?boolean默认:true,是否对输出进行 HTML 转义。
      • views?string,视图路径,用于 include 的查找。
    • 返回:

    • compiledFunctionobject,是一个接受数据对象并返回渲染后的 HTML 字符串的函数。

      js
      const ejs = require('ejs');
      
      // 模板字符串
      const templateString = 'Hello, <%= name %>!';
      
      // 编译模板
      const compiledFunction = ejs.compile(templateString, {
        filename: 'template.ejs',
        cache: true,
        delimiter: '%',
        escape: false
      });
      
      // 渲染模板
      const html = compiledFunction({ name: 'World' });
      
      console.log(html); // 输出: Hello, World!

webpack

API:

  • require.context()(directory, useSubdirectories, regExp),是 Webpack 特有的一个函数,用于动态加载模块。它在构建时会创建一个上下文,帮助你动态地导入文件。

    • directorystring,指定要搜索的目录路径,相对路径。

    • useSubdirectoriesboolean,是否搜索子目录。

    • regExpreg,只加载与正则表达式匹配的文件。

    • 返回:

    • context(item) => Module,返回一个上下文函数,用于动态加载模块。

    • js
      const context = require.context('./services', false, /\.js$/);
      
      const services = context.keys().reduce((acc, key) => {
        const serviceName = key.replace(/^.*[\\/]/, '').replace(/\.js$/, '');
        acc[serviceName] = context(key);  // 动态加载模块
        return acc;
      }, {});
      
      console.log(services);
      
      // {SomeService: './services/SomeService.js', ...}
  • context.keys()(),用于获取匹配的模块路径数组。

    • context,是通过 require.context() 创建的上下文对象。

    • 返回:

    • keysstring[],返回包含了所有与 require.context() 创建的上下文对象匹配的模块相对路径字符串。

    • js
      // 创建一个上下文来导入 ./src 目录下所有以 .js 结尾的文件
      const context = require.context('./src', false, /\.js$/);
      
      // 获取所有匹配的文件路径
      const keys = context.keys();
      
      console.log(keys);
      // 输出:['./file1.js', './file2.js', './subdir/file3.js']
  • context()(path),是 require.context() 返回的一个函数,用于动态加载模块。

    • pathstring,模块的路径,相对于上下文的目录。路径必须匹配在 require.context() 时指定的正则表达式。

    • 返回:

    • moduleModule,返回值是匹配路径的模块对象。这允许你在运行时动态地导入模块,而不是在编译时静态地导入它们。

    • js
      const context = require.context('./modules', false, /\.js$/);
      
      const moduleA = context('./moduleA.js');  // 加载 ./modules/moduleA.js
      console.log(moduleA);

说明文档

目前前端工程化开发过程中,我们会使用各种各样的脚手架,vue-cli、create-react-app,当然也包括 webpack、gulp、rollup、vite 等工具。

这些工具是怎么开发出来的呢?当我们执行一个命令时,它们做了什么事情?是怎么样完成的一系列操作?

这里我开发了一个 coderwhy 的脚手架:一个帮助你快速搭建和开发前端项目的 CLI。

文档内容分成两部分:

  • 第一部分:coderwhy 使用说明;

  • 第二部分:coderwhy 脚手架开发过程;

欢迎下载学习,如果对你有帮助,可以点一个 star~

coderwhy

想不起来其他名字,以这个命名吧~

coderwhy: 一个帮助你快速搭建和开发前端项目的 CLI

如何安装?

sh
npm install coderwhy -g

创建项目

目前支持 Vue,后期会支持 React,Angular 考虑中~

1、功能

vue 项目模块已经帮你配置:

  • 常用的目录结构(你可以在此基础上修改)
  • vue.config.js(其中配置了别名,你可以自行修改和配置更多)
  • axios(网络请求 axios 的安装以及二次封装)
  • vue-router(router 的安装和配置,另外有路由的动态加载,后面详细说明)
  • vuex(vuex 的安装和配置,另外有动态加载子模块,后面详细说明)

2、使用coderwhy创建项目

sh
coderwhy create <your_project_name>

自动拉取项目模板、安装项目依赖、打开浏览器 http://localhost:8080/、自动启动项目

项目开发

项目开发目前提供三个功能:

  • 创建 Vue 组件
  • 创建 Vue 页面,并配置路由
  • 创建 Vuex 子模块

1、创建 Vue 组件

sh
# 例如coderwhy addcpn NavBar,默认会存放到src/components文件夹中
coderwhy addcpn YourComponentName 

# 也可以指定存放的具体文件夹
coderwhy addcpn YourComponentName -d src/pages/home

2、创建 Vue 页面,并配置路由

sh
# 例如coderwhy addpage Home,默认会放到src/pages/home/Home.vue中,并且会创建src/page/home/router.js
coderwhy addpage YourPageName 

# 也可以指定文件夹,但需要手动集成路由
coderwhy addpage YourPageName -d src/views

为什么会创建 router.js 文件:

  • router.js文件是路由的其中一个配置;
  • 创建该文件中 src/router/index.js中会自动加载到路由的 routes配置中,不需要手动配置了(如果是自己配置的文件夹需要手动配置)

src/router/index.js中已经完成如下操作:

js
// 动态加载pages中所有的路由文件
const context = require.context('@/pages', true, /router\.js$/)
const routes = context.keys().map((key) => {
  const page = require('@/pages' + key.replace('.', ''))
  return page.default
})

3、创建 Vuex 子模块

sh
# 例如coderwhy addstore home,默认会放到src/store/modules/home/index.js和types.js
coderwhy addstore YourVuexChildModuleName 

# 也可以指定文件夹
coderwhy addstore YourVuexChildModuleName -d src/vuex/modules

创建完成后,不需要手动配置,已经动态将所有子模块集成进去:

js
// 动态加载modules
const modules = {}
const context = require.context('./', true, /index\.js$/)
context
  .keys()
  .filter((key) => {
    if (key === './index.js') return false
    return true
  })
  .map((key) => {
    // 获取名字
    const modulePath = key.replace('./modules/', '')
    const moduleName = modulePath.replace('/index.js', '')
    const module = require(`${key}`)

    modules[`${moduleName}`] = module.default
  })

开发过程

项目开始

1、初始化项目

sh
pnpm init

image-20240910111033770

2、创建 ./lib/index.js 文件

js
// ./lib/index.js
console.log('Hello Coderwhy')

创建 package.json

json
{
  "name": "coderwhy",
  "version": "1.1.0",
  "description": "CLI front-end development tools",
  "main": "index.js",
  "bin": {
    "coderwhy": "index.js"
  },
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": ["vue", "react", "CLI", "component"],
  "author": "coderwhy",
  "license": "MIT",
  "homepage": "https://github.com/coderwhy/coderwhy",
  "repository": {
    "type": "git",
    "url": "https://github.com/coderwhy/coderwhy"
  },
  "dependencies": {
    "chalk": "^4.1.0",
    "commander": "^6.1.0",
    "download-git-repo": "^3.0.2",
    "ejs": "^3.1.5",
    "open": "^7.3.0"
  }
}

最终的目录结构:

sh
├── LICENSE
├── index.js
├── lib
   ├── config
   └── repo_config.js
   ├── core
   ├── actions.js
   ├── create.js
   └── help.js
   ├── template
   ├── component.vue.ejs
   ├── vue-router.js.ejs
   ├── vue-store.js.ejs
   └── vue-types.js.ejs
   └── utils
       ├── file.js
       ├── log.js
       └── terminal.js
├── package-lock.json
├── package.json
└── readme.md

创建 coderwhy 的命令

1、创建bin文件bin/vite.js

#!/usr/bin/env node:这是bash命令,会自动在环境变量中查找node,使用node运行vite.js代码

注意:必须放在第一行

js
#!/usr/bin/env node

2、在 package.json中添加bin选项

当在命令行输入coderwhy就会来到bin/vite.js文件并执行其中的代码

js
  "bin": {
    "coderwhy": "index.js"
  }

3、执行 npm link,建立一个软链接。将coderwhy添加到环境变量中

image-20240910112536027

解析命令行参数

node解析命令行参数

在node中内置了对命令行参数的解析:

1、在命令行输入命令并携带参数

sh
coderwhy --version

2、在node代码中可以通过process.argv获取到参数

image-20240910113326686

image-20240910113332511

commander解析命令行参数

依赖包: commander :完整的 node.js 命令行解决方案。

  • 安装:pnpm i commander

一、定义版本号

js
#!/usr/bin/env node
// 
const { cmd } = require('commander') // 推荐该方式导入,vscode提示会更好
// const cmd = require('commander') // 此处2种方式都可以导入cmd

// 定义显示模块的版本号
cmd.version(require('./package.json').version, '-v --version')

// 解析终端指令
cmd.parse(process.argv)

image-20240910115021524

二、给help增加其他选项

1、添加单个选项

js
program.option('-s --src <src>', 'a source folder')
program.option('-d --dest <dest>', 'a destination folder')
program.option('-f --framework <framework>', 'your framework name')

image-20240910123439344

2、自定义 help 指令,添加额外的帮助信息

js
program.on('--help', function () {
  console.log('')
  console.log('Usage:')
  console.log('   coderwhy -v')
  console.log('   coderwhy --version')
})

image-20240910124525099

3、封装option选项

  • 封装

    image-20240910130058694

  • 导入并使用

    image-20240910130121223

  • 命令行使用

    image-20240910130218379

创建项目指令create

依赖包:

  • download-git-repo:用于从 Git 仓库中下载项目或文件。只下载指定的版本或分支,而不涉及 Git 版本控制系统本身的操作。
  • 安装:pnpm i download-git-repo

创建项目:

1、在命令行使用create命令

sh
$ coderwhy create airbnb

2、创建命令

js
// 创建命令
program
  .command('create <project> [otherArgs...]')
  .description('clone a repository into a newly created directory')
+  .action(createProject)

3、在 actions 中封装创建过程:

  • promisify():用于将遵循回调模式的异步函数转换为返回 Promise 的函数。
js
const downloadRepoAsync = promisify(require('download-git-repo'))

const createProject = async (project, otherArg) => {
  // 1.提示信息
  console.log('coderwhy helps you create your project, please wait a moment~')

  // 2.从仓库clone项目
  await downloadRepoAsync(repoConfig.vueGitRepo, project, { clone: true })

  // 3.执行终端命令npm install
  // terminal.exec('npm install', {cwd: `./${project}`});
  const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'
  await terminal.spawn(npm, ['install'], { cwd: `./${project}` })

  // 4.打开浏览器
  open('http://localhost:8080/')

  // 5.运行项目
  await terminal.spawn(npm, ['run', 'serve'], { cwd: `./${project}` })
}

4、配置的 Git 地址如下:

  • 后续会开发一个设置自己地址的指令
js
// 注意:地址上需要添加`direct:`和`#main`,否则报错
const vueGitRepo = 'direct:https://github.com/coderwhy/hy-vue-temp.git#main'

module.exports = {
  vueGitRepo
}

5、封装执行终端命令的过程:

js
const { spawn, exec } = require('child_process')

const execCommand = (...cmds) => {
  return new Promise((resolve, reject) => {
    // 1. 开启子进程执行命令
    const childProcess = spawn(...cmds)
    
    // 2. 获取子进程的输出和错误信息
    childProcess.stdout.pipe(process.stdout)
    childProcess.stderr.pipe(process.stderr)
      
    // 3. 监听子进程执行结束
    childProcess.on('close', () => {
      resolve()
    })
  })
}

image-20240910161148057

添加组件指令

创建添加指令

1、添加指令

js
program
  .command('addcpn <name> [...others]')
  .description('add vue component, 例如: coderwhy addcpn NavBar -d src/components')
+  .action((name) => addComponent(name, program.opts().dest || 'src/components'))

program
  .command('addpage <name> [...others]')
  .description('add vue page, 例如: coderwhy addpage Home -d dest')
  .action((name) => {
+    addPage(name, program.opts().dest || `src/pages/${name.toLowerCase()}`)
  })

program
  .command('addstore <name> [...others]')
  .description('add vue store, 例如: coderwhy addstore favor[-d dest')
  .action((name) => {
+    addStore(name, program.opts().dest || `src/store/modules/${name.toLowerCase()}`)
  })

2、封装对应的 action

js
const addComponent = async (name, dest) => {
+  handleEjsToFile(name, dest, '../template/component.vue.ejs', `${name}.vue`)
}

const addPage = async (name, dest) => {
  addComponent(name, dest)
+  handleEjsToFile(name, dest, '../template/vue-router.js.ejs', 'router.js')
}

const addStore = async (name, dest) => {
+  handleEjsToFile(name, dest, '../template/vue-store.js.ejs', 'index.js')
+  handleEjsToFile(name, dest, '../template/vue-types.js.ejs', 'types.js')
}
封装 ejs 模板

依赖插件:

补充知识: EJS

1、组件模板:component.vue.ejs

vue
<%_ if(data) { _%>
<template>
  <div class="<%= data.lowerName %>">
    <h2>{{ message }}</h2>
  </div>
</template>

<script>
  export default {
    name: "<%= data.name %>",
    components: {

    },
    mixins: [],
    props: {

    },
    data: function() {
      return {
        message: "Hello <%= data.name %>"
      }
    },
    created: function() {

    },
    mounted: function() {

    },
    computed: {

    },
    methods: {

    }
  }
</script>

<style scoped>
  .<%= data.lowerName %> {

  }
</style>

<%_ } _%>

2、路由模板:component.vue.ejs

  • 组件模板,直接使用上面的即可
  • router.js 模板
vue
<%_ if (data) { _%>
// 普通加载路由
// import <%= data.name %> from './<%= data.name %>.vue'
// 懒加载路由
const <%= data.name %> = () => import('./<%= data.name %>.vue')
export default {
  path: '/<%= data.lowerName %>',
  name: '<%= data.name %>',
  component: <%= data.name %>,
  children: [
  ]
}
<%_ } _%>

3、vuex 模块的模板:component.vue.ejs

  • index.js 模板
  • types.js 模板

index.js 模块

vue
import * as types from './types.js'
export default {
  namespaced: true,
  state: {
  },
  mutations: {
  },
  actions: {
  },
  getters: {
  }
}

types.js 模块

vue
export {

}
封装 ejs 解析

依赖包:

  • ejs:EJS语法的扩展API
    • 安装:pnpm i ejs

1、封装 ejs 到文件的转化过程:handleEjsToFile()

js
const handleEjsToFile = async (name, dest, template, filename) => {
  // 1.获取模块引擎的路径
  const templatePath = path.resolve(__dirname, template)
+  const result = await ejsCompile(templatePath, { name, lowerName: name.toLowerCase() })

  // 判断文件不存在,那么就创建文件
+  mkdirSync(dest)
    
  // 写入文件中
  const targetPath = path.resolve(dest, filename)
+ await writeFile(targetPath, result)
  
  console.log('创建组件成功:' + name)
}

2、封装 ejs 的编译过程:ejsCompile()

js
const ejsCompile = (templatePath, data = {}, options = {}) => {
  return new Promise((resolve, reject) => {
    ejs.renderFile(templatePath, { data }, options, (err, str) => {
      if (err) {
        reject(err)
        return
      }
      resolve(str)
    })
  })
}

3、封装创建文件夹的过程:mkdirSync()

js
const mkdirSync = (dirname) => {
  if (fs.existsSync(dirname)) {
    return true
  } else {
    // 不存在,判断父文件夹是否存在?
    if (mkdirSync(path.dirname(dirname))) {
      // 存在父文件,就直接新建该文件
      fs.mkdirSync(dirname)
      return true
    }
  }
}

4、封装写入文件的过程:writeFile()

js
const writeFile = (path, content) => {
  if (fs.existsSync(path)) {
    log.error('the file already exists~')
    return
  }
  return fs.promises.writeFile(path, content)
}

发布工具

注册 npm 账号:

sign up 注册

image-20240719162101291

在命令行登录:

sh
npm login
# 输入账号、密码、邮箱

修改好 package.json 文件:

json
+  "keywords": [
    "vue",
    "react",
    "CLI",
    "component"
  ],
+  "author": "coderwhy",
+  "license": "MIT",
+  "homepage": "https://github.com/coderwhy/coderwhy",
+  "repository": {
    "type": "git",
    "url": "https://github.com/coderwhy/coderwhy"
  },

发布到 npm registry 中

sh
npm publish

更新 registry

sh
# 1.修改版本号(最好符合semver规范)
# 2.重新发布

删除发布的包:

sh
npm unpublish

过期发布的包:

sh
npm deprecate